Skip to content

Dev/2.3.0#65

Merged
ink-developer merged 7 commits into
mainfrom
dev/2.3.0
Jun 18, 2026
Merged

Dev/2.3.0#65
ink-developer merged 7 commits into
mainfrom
dev/2.3.0

Conversation

@ink-developer

@ink-developer ink-developer commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

Описание

Подготавливает релиз PyMax 2.3.0: добавляет release notes, поднимает версию пакета и дополняет документацию по новым публичным API.

В релиз вошли:

  • несколько on_start-обработчиков;
  • on_error() с ErrorContext и ErrorScope;
  • on_disconnect();
  • relogin();
  • delete_chat() и Chat.delete();
  • import_contacts() и ContactInfo;
  • SessionStore.delete_all_sessions();
  • переход edit_message() / Message.edit() на attachments=[...].

Тип изменений

  • Исправление бага
  • Новая функциональность
  • Улучшение документации
  • Рефакторинг

Связанные задачи / Issue

Ссылка на issue, если есть: #

Summary by CodeRabbit

Release Notes

  • New Features

    • Multiple on_start handlers now execute after successful login
    • Centralized error handling with on_error() supporting global/local scopes
    • on_disconnect() handler for connection state changes
    • relogin() method for session refresh and re-authentication
    • Chat deletion with delete_chat()
    • Contact import functionality via import_contacts()
    • Full session clearing capability
  • Breaking Changes

    • Message editing now uses only attachments parameter (removed singular attachment)
    • Attachment parameters now accept any Sequence type
  • Documentation

    • Comprehensive release notes and API documentation updates

@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b6b64abf-dfba-49fc-aded-80d473c0bedc

📥 Commits

Reviewing files that changed from the base of the PR and between fe1c5ab and 6c6f1d7.

📒 Files selected for processing (4)
  • docs/release-2-3-0.rst
  • src/pymax/api/chats/service.py
  • src/pymax/base.py
  • src/pymax/dispatch/router.py
✅ Files skipped from review due to trivial changes (1)
  • docs/release-2-3-0.rst
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/pymax/base.py
  • src/pymax/dispatch/router.py
  • src/pymax/api/chats/service.py

📝 Walkthrough

Walkthrough

PyMax 2.3.0 adds centralized on_error/on_disconnect dispatcher callbacks with ErrorScope semantics, a relogin() flow on BaseClient, delete_chat()/Chat.delete(), import_contacts() with a new ContactInfo model, SessionStore.delete_all_sessions(), and removes the singular attachment parameter from edit_message/Message.edit in favor of attachments: Sequence[...]. Tests, documentation, and version metadata are updated throughout.

Changes

PyMax 2.3.0 Feature Release

Layer / File(s) Summary
Error/disconnect type contracts and Router registration APIs
src/pymax/dispatch/router.py, src/pymax/dispatch/enums.py, src/pymax/dispatch/__init__.py
Introduces ErrorScope enum, ErrorContext dataclass, ErrorEntry, ErrorCallback/ErrorDecorator, DisconnectCallback/DisconnectDecorator type aliases, and ErrorSource. Adds on_error(scope) and on_disconnect() decorator methods to Router. Updates on_start() to append to a list of handlers. Adds EventType.ON_START. Expands dispatch.__all__.
Dispatcher emit_error, emit_disconnect, and startup refactor
src/pymax/dispatch/dispatcher.py
Rewrites emit_start to iterate on_start_handlers and route failures through emit_error. Adds iter_error_entries, iter_disconnect_handlers, iter_error_handlers, emit_error (builds ErrorContext, iterates scoped handlers, returns handled bool), and emit_disconnect (iterates handlers, logs per-handler exceptions). Wraps _dispatch_to_router invocation in try/except routing to emit_error.
App startup error handling and BaseClient runtime changes
src/pymax/app.py, src/pymax/base.py
Wraps App.start login call: on failure calls dispatcher.emit_error; if handled, closes and returns early; otherwise re-raises. BaseClient.start exits loop early if _app.started is falsy. Connection exception path emits dispatcher.emit_disconnect before re-raise or reconnect sleep. Adds on_error() and on_disconnect() registration methods to BaseClient.
relogin and SessionStore.delete_all_sessions
src/pymax/base.py, src/pymax/session/store.py
SessionStore.delete_all_sessions() deletes all rows from the sessions table. BaseClient.relogin() requires a loaded session, deletes the session token, optionally clears config tokens, resets runtime, and optionally calls start().
delete_chat API: payload, service, Chat.delete, and ChatMixin
src/pymax/api/chats/payloads.py, src/pymax/api/chats/service.py, src/pymax/types/domain/chat.py, src/pymax/infra/chat.py, docs/chats.rst
Adds DeleteChatPayload model. ChatService.delete_chat builds payload, invokes CHAT_DELETE opcode, removes chat from cache. Chat.delete binds the service and calls delete_chat with last_event_time and for_all. ChatMixin.delete_chat delegates to the API service. Docs updated.
import_contacts API: ContactInfo, payloads, service, and UserMixin
src/pymax/types/domain/user.py, src/pymax/types/domain/__init__.py, src/pymax/api/users/payloads.py, src/pymax/api/users/service.py, src/pymax/infra/user.py, docs/users.rst, docs/types/*
Adds ContactInfo domain model. Exports from domain/__init__. Adds ImportContactsPayload with from_contacts classmethod. UserService.import_contacts invokes SYNC opcode, parses CONTACTS response, caches and returns users. UserMixin.import_contacts delegates to the service. ContactInfo docs and API reference page added.
edit_message attachment API cleanup
src/pymax/types/domain/message.py, src/pymax/api/messages/service.py, src/pymax/infra/message.py, docs/messages.rst
SendAttachments type alias changed from list to Sequence. MessageService.edit_message, MessageMixin.edit_message, and Message.edit remove the singular attachment parameter; attachments: SendAttachments is the only attachment argument. Docs updated.
Tests for all new features
tests/app/test_app_runtime.py, tests/api/test_chat_user_self_session_services.py, tests/api/test_message_service.py, tests/domain/test_bound_models.py, tests/session/test_store.py
Adds RuntimeClient and extended RuntimeStore. Tests cover: login error emitting on_error and suppressing on_start; connection drop emitting on_disconnect before re-raise; emit_disconnect handler exceptions not aborting iteration; relogin deleting only the loaded session; delete_chat cache eviction and payload; import_contacts SYNC payload; edit_message attachments-only API; Chat.delete and Message.edit delegation; SessionStore.delete_all_sessions.
2.3.0 release notes, updated docs, and version bump
pyproject.toml, src/pymax/__init__.py, docs/release-2-3-0.rst, docs/router.rst, docs/client.rst, docs/api/router.rst, docs/index.rst, docs/messages.rst
Bumps version to 2.3.0. Adds release notes with added/changed/migration sections. Expands router.rst with multi-handler on_start, on_error scope semantics, and on_disconnect docs. Updates client.rst with relogin and on_disconnect examples. Adds ErrorScope/ErrorContext to API router docs. Updates index toctree.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • MaxApiTeam/PyMax#64: Introduced the edit_message/Message.edit attachment API that this PR further modifies by removing the singular attachment parameter and updating SendAttachments to Sequence.

Poem

🐇 Hoppity-hop through error scopes wide,
on_error and on_disconnect now bide!
relogin() sheds the old session's shell,
delete_chat, import_contacts — all is well.
The rabbit bumps versions with carrot-fueled pride,
2.3.0 hops forth on a glorious ride! 🥕

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 24.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Dev/2.3.0' is vague and generic, using a branch-naming convention rather than a clear summary of the changeset's main purpose. Use a descriptive title that summarizes the main change, such as 'Release PyMax 2.3.0 with new event handlers and chat management features' or 'Prepare PyMax 2.3.0 release with updated documentation and new APIs'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The description is well-structured, clearly lists included features, and checks appropriate change type checkboxes ('New functionality' and 'Documentation improvement'). All required sections are present and substantially filled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev/2.3.0

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
src/pymax/api/users/payloads.py (1)

30-37: ⚡ Quick win

Define explicit duplicate-phone behavior in from_contacts.

Line 32-37 currently overwrites earlier entries when two ContactInfo items share the same phone. Please make this explicit (reject duplicates or intentionally keep first/last) to avoid silent payload data loss.

♻️ Proposed change
 class ImportContactsPayload(CamelModel):
     contact_list: dict[str, _ContactPayload]  # phone -> contact payload

     `@classmethod`
     def from_contacts(cls, contacts: Iterable[ContactInfo]) -> "ImportContactsPayload":
-        return cls(
-            contact_list={
-                contact.phone: _ContactPayload(
-                    first_name=contact.first_name,
-                )
-                for contact in contacts
-            }
-        )
+        contact_list: dict[str, _ContactPayload] = {}
+        for contact in contacts:
+            if contact.phone in contact_list:
+                raise ValueError(f"Duplicate phone in contacts: {contact.phone}")
+            contact_list[contact.phone] = _ContactPayload(first_name=contact.first_name)
+        return cls(contact_list=contact_list)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pymax/api/users/payloads.py` around lines 30 - 37, The from_contacts
class method in ImportContactsPayload currently creates a dictionary with phone
as the key, which silently overwrites earlier contact entries when duplicate
phone numbers exist. Add explicit duplicate-phone handling logic before or
within the dictionary comprehension to either reject duplicates by raising an
exception, or intentionally keep the first or last occurrence of each phone
number. Make your chosen behavior clear in the code with appropriate comments or
conditional logic to prevent silent data loss.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/pymax/api/chats/service.py`:
- Around line 367-377: The delete_chat method uses a truthiness check
(`last_event_time or int(time.time() * 1000)`) which incorrectly treats 0 as
falsy and replaces it with the current timestamp. Fix this by replacing the `or`
operator with an explicit `None` check using a conditional expression (ternary
operator or `if last_event_time is None else`) to allow 0 as a valid value for
the last_event_time parameter in the DeleteChatPayload instantiation.

In `@src/pymax/base.py`:
- Around line 277-279: The code closes the store via self.close() and then
attempts to call delete_session() on the already-closed store, followed by
calling store.close() again. Reorder the operations to call delete_session() on
the store before invoking self.close(), then remove the redundant await
store.close() call since self.close() already closes the store that was
captured. This ensures the session is deleted while the store is still open and
avoids mutating a closed resource.

In `@src/pymax/dispatch/router.py`:
- Line 35: Update the test type annotations in tests/app/test_app_runtime.py to
satisfy the new ClientT bound to BaseClient. Replace all instances of
Router[object] and App[object] at lines 138, 182-183, 310-311, 385, 400, and 432
with either Any (if you want to disable type checking for those tests) or a
concrete BaseClient subclass that properly implements the BaseClient interface.
This will resolve the type constraint violation introduced by the new TypeVar
bound in the router.py file.
- Around line 142-153: The on_error method in the router class accepts a scope
parameter that can be passed as a raw string (since ErrorScope subclasses str),
but the current implementation stores it as-is in the error_handlers list via
ErrorEntry. Downstream code uses identity comparison (is operator) to check
scope values, which fails for raw strings. Normalize the scope parameter by
wrapping it with ErrorScope(scope) before creating the ErrorEntry and appending
it to self.error_handlers to ensure identity checks work correctly and
LOCAL-scoped handlers are not incorrectly treated as GLOBAL.

In `@src/pymax/session/protocol.py`:
- Line 14: The `delete_all_sessions()` method should be removed from the
`StoreProtocol` interface definition in the protocol.py file. This method is not
required by the relogin path which only uses `delete_session()`, and adding it
to the protocol breaks backward compatibility with existing custom store
implementations. Keep the bulk-purge functionality as a concrete implementation
detail in specific store classes that need it, rather than as part of the
protocol contract.

---

Nitpick comments:
In `@src/pymax/api/users/payloads.py`:
- Around line 30-37: The from_contacts class method in ImportContactsPayload
currently creates a dictionary with phone as the key, which silently overwrites
earlier contact entries when duplicate phone numbers exist. Add explicit
duplicate-phone handling logic before or within the dictionary comprehension to
either reject duplicates by raising an exception, or intentionally keep the
first or last occurrence of each phone number. Make your chosen behavior clear
in the code with appropriate comments or conditional logic to prevent silent
data loss.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1e288c93-8587-4413-a63f-64eb78981883

📥 Commits

Reviewing files that changed from the base of the PR and between 78b7ffe and fe1c5ab.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (37)
  • docs/api/router.rst
  • docs/chats.rst
  • docs/client.rst
  • docs/index.rst
  • docs/messages.rst
  • docs/release-2-3-0.rst
  • docs/router.rst
  • docs/types/contact_info.rst
  • docs/types/index.rst
  • docs/users.rst
  • pyproject.toml
  • src/pymax/__init__.py
  • src/pymax/api/chats/payloads.py
  • src/pymax/api/chats/service.py
  • src/pymax/api/messages/service.py
  • src/pymax/api/users/payloads.py
  • src/pymax/api/users/service.py
  • src/pymax/app.py
  • src/pymax/base.py
  • src/pymax/dispatch/__init__.py
  • src/pymax/dispatch/dispatcher.py
  • src/pymax/dispatch/enums.py
  • src/pymax/dispatch/router.py
  • src/pymax/infra/chat.py
  • src/pymax/infra/message.py
  • src/pymax/infra/user.py
  • src/pymax/session/protocol.py
  • src/pymax/session/store.py
  • src/pymax/types/domain/__init__.py
  • src/pymax/types/domain/chat.py
  • src/pymax/types/domain/message.py
  • src/pymax/types/domain/user.py
  • tests/api/test_chat_user_self_session_services.py
  • tests/api/test_message_service.py
  • tests/app/test_app_runtime.py
  • tests/domain/test_bound_models.py
  • tests/session/test_store.py

Comment thread src/pymax/api/chats/service.py
Comment thread src/pymax/base.py Outdated

_EventT = TypeVar("_EventT")
ClientT = TypeVar("ClientT")
ClientT = TypeVar("ClientT", bound="BaseClient")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Find generic Router/App usages that violate ClientT's BaseClient bound.
# Expect: No Router[object] / App[object] usages remain, or they are intentionally changed to Any/concrete client types.

rg -nP -C2 '\b(Router|App)\s*\[\s*object\s*\]'

Repository: MaxApiTeam/PyMax

Length of output: 2175


Update test type annotations to match the new BaseClient bound.

Multiple test helpers still use Router[object] and App[object] (lines 138, 182–183, 310–311, 385, 400, 432 in tests/app/test_app_runtime.py), which violate the ClientT bound to BaseClient. Either use Any for type annotations, provide a concrete BaseClient subclass, or reconsider the bound if these classes should accept arbitrary client types.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pymax/dispatch/router.py` at line 35, Update the test type annotations in
tests/app/test_app_runtime.py to satisfy the new ClientT bound to BaseClient.
Replace all instances of Router[object] and App[object] at lines 138, 182-183,
310-311, 385, 400, and 432 with either Any (if you want to disable type checking
for those tests) or a concrete BaseClient subclass that properly implements the
BaseClient interface. This will resolve the type constraint violation introduced
by the new TypeVar bound in the router.py file.

Comment thread src/pymax/dispatch/router.py
Comment thread src/pymax/session/protocol.py Outdated
@ink-developer ink-developer merged commit ff3af5b into main Jun 18, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant